import arrayPrototype from "./array";

class Observer {
  constructor(data) {

    // __ob__ 表示数据是已经被观测的对象
    // data.__ob__ = this; // 这样会导致内存溢出，需要把 data 上挂载的 __ob__ 设置为不可以枚举即可
    // 设置 data.__ob__ 不可枚举的，解决方案：
    Object.defineProperty(data, '__ob__', {
      value: this,
      enumerable: false, // 设置为不可枚举的
    })


    // 如果 data 是数组, 不需要对索引也观测（vue2消耗性能的太大），重写数组的方法（push,pop,shift,unshift,reverse,sort,splice）实现数据响应式
    // vue2 对数组的长度和索引是没有观测的，所以不要通过索引和修改数据长度来实现响应式的数据变化
    if (Array.isArray(data)) {
      // 观察数组，重写 7 个数组的原生方法 push,pop,shift,unshift,reverse,sort,splice
      data.__proto__ = arrayPrototype;
      // 如果数组中存放是是对象，也观测它，变成响应式
      this.observeArray(data);
    } else {
      // 遍历观测对象
      this.walk(data);
    }
  }

  // 观测数组中的对象
  observeArray(data) {
    data.forEach(item => observe(item));
  }

  // 观测对象
  walk(data) {
    const keys = Object.keys(data);
    keys.forEach(key => {
      defineReactive(data, key, data[key]);
    })
  }
}

// Vue2 的响应式原理的核心方法 defineReactive
function defineReactive(data, key, value) {

  observe(value); // 递归深度遍历属性，进行观测（vue2消耗性能的一个地方）
  Object.defineProperty(data, key, {
    get() {
      // console.log('get')
      return value;
    },
    set(newValue) {
      // console.log('set')

      if (newValue === value) return;
      // 如果用户通过 vm 设置属性，也进行观测,实现响应式
      observe(newValue);

      value = newValue;

    }
  });
}

export function observe(data) {
  // 不是对象，或者为 null / undefined 就返回
  if (typeof data !== "object" || data == null) return;

  // __ob__ 标识是否已经被观测过
  if (data.__ob__) return data;


  return new Observer(data);
}